home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / shells / zsh-3.0-p / zsh-3 / zsh-3.0-pre3 / Src / zle_refresh.c < prev    next >
C/C++ Source or Header  |  1996-07-15  |  27KB  |  987 lines

  1. /*
  2.  * $Id: zle_refresh.c,v 2.6 1996/07/14 18:58:54 hzoli Exp $
  3.  *
  4.  * zle_refresh.c - screen update
  5.  *
  6.  * This file is part of zsh, the Z shell.
  7.  *
  8.  * Copyright (c) 1992-1996 Paul Falstad
  9.  * All rights reserved.
  10.  *
  11.  * Permission is hereby granted, without written agreement and without
  12.  * license or royalty fees, to use, copy, modify, and distribute this
  13.  * software and its documentation for any purpose, provided that the
  14.  * above copyright notice and the following two paragraphs appear in
  15.  * all copies of this software.
  16.  *
  17.  * In no event shall Paul Falstad or the Zsh Development Group be liable
  18.  * to any party for direct, indirect, special, incidental, or consequential
  19.  * damages arising out of the use of this software and its documentation,
  20.  * even if Paul Falstad and the Zsh Development Group have been advised of
  21.  * the possibility of such damage.
  22.  *
  23.  * Paul Falstad and the Zsh Development Group specifically disclaim any
  24.  * warranties, including, but not limited to, the implied warranties of
  25.  * merchantability and fitness for a particular purpose.  The software
  26.  * provided hereunder is on an "as is" basis, and Paul Falstad and the
  27.  * Zsh Development Group have no obligation to provide maintenance,
  28.  * support, updates, enhancements, or modifications.
  29.  *
  30.  */
  31.  
  32. #define ZLE
  33. #include "zsh.h"
  34.  
  35. #ifdef HAVE_SELECT
  36. #define SELECT_ADD_COST(X)    cost += X
  37. #else
  38. #define SELECT_ADD_COST(X)
  39. #endif
  40.  
  41. /* Oct/Nov 94: <mason> some code savagely redesigned to fix several bugs -
  42.    refreshline() & tc_rightcurs() majorly rewritten; refresh() fixed -
  43.    I've put my fingers into just about every routine in here -
  44.    any queries about updates to mason@werple.net.au */
  45.  
  46. char **nbuf = NULL,        /* new video buffer line-by-line char array */
  47.     **obuf = NULL;        /* old video buffer line-by-line char array */
  48. static char *lpptbuf, *rpptbuf; /* prompt buffers                */
  49. static int baudrate,        /* BAUD                        */
  50.     more_start,            /* more text before start of screen?        */
  51.     more_end,            /* more stuff after end of screen?        */
  52.     olnct,            /* previous number of lines            */
  53.     ovln,            /* previous video cursor position line        */
  54.     lpptlen, rpptlen,           /* length of prompt buffers                 */
  55.     pptw, rpw,                  /* prompt widths on screen                  */
  56.     vcs, vln,            /* video cursor position column & line        */
  57.     vmaxln,            /* video maximum number of lines        */
  58.     winw, winh,            /* window width & height            */
  59.     winpos;            /* singlelinezle: line's position in window */
  60. static unsigned pmpt_attr = 0,    /* text attributes after displaying prompt  */
  61.     rpmpt_attr = 0;        /* text attributes after displaying rprompt */
  62.  
  63. /**/
  64. void
  65. resetvideo(void)
  66. {
  67.     int ln;
  68.     static int lwinw = -1, lwinh = -1;    /* last window width & height */
  69.  
  70.     winw = (columns < 1) ? (columns = 80) : columns;  /* terminal width */
  71.     if (isset(SINGLELINEZLE) || termok != TERM_OK)
  72.     winh = 1;
  73.     else
  74.     winh = (lines < 2) ? 24 : lines;
  75.     winpos = vln = vmaxln = 0;
  76.     if (lwinw != winw || lwinh != winh) {
  77.     if (nbuf) {
  78.         for (ln = 0; ln != lwinh; ln++) {
  79.         zfree(nbuf[ln], lwinw + 1);
  80.         zfree(obuf[ln], lwinw + 1);
  81.         }
  82.         free(nbuf);
  83.         free(obuf);
  84.     }
  85.     nbuf = (char **)zcalloc((winh + 1) * sizeof(char *));
  86.     obuf = (char **)zcalloc((winh + 1) * sizeof(char *));
  87.     nbuf[0] = (char *)zalloc(winw + 1);
  88.     obuf[0] = (char *)zalloc(winw + 1);
  89.  
  90.     lwinw = winw;
  91.     lwinh = winh;
  92.     }
  93.     for (ln = 0; ln != winh + 1; ln++) {
  94.     if (nbuf[ln])
  95.         *nbuf[ln] = '\0';
  96.     if (obuf[ln])
  97.         *obuf[ln] = '\0';
  98.     }
  99.  
  100.     if (pptw) {
  101.         memset(nbuf[0], ' ', pptw);
  102.     memset(obuf[0], ' ', pptw);
  103.     nbuf[0][pptw] = obuf[0][pptw] = '\0';
  104.     }
  105.  
  106.     vcs = pptw;
  107.     olnct = nlnct = 0;
  108.     if (showinglist > 0)
  109.     showinglist = -2;
  110. }
  111.  
  112. /*
  113.  * Jul 96: changed to single line scroll for higher speed terminals - mason
  114.  *  I've seperated the loops for readability (and it's slightly faster)
  115.  *  Returns line number to be used next
  116.  */
  117.  
  118. /**/
  119. int
  120. scrollwindow(int tline)
  121. {
  122.     int t0, hwinh;
  123.     char *s;
  124.  
  125.     if (tline || baudrate >= 9600) {    /* single line scroll */
  126.     hwinh = 1;
  127.     s = nbuf[tline];
  128.     for (t0 = tline; t0 < winh - 1; t0++)
  129.         nbuf[t0] = nbuf[t0 + 1];
  130.     nbuf[winh - 1] = s;
  131.     } else {                 /* half screen scroll */
  132.     hwinh = winh / 2;
  133.     for (t0 = 0; t0 != winh - hwinh; t0++) {
  134.         s = nbuf[t0];
  135.         nbuf[t0] = nbuf[t0 + hwinh];
  136.         nbuf[t0 + hwinh] = s;
  137.     }
  138.     }
  139.     if (!tline)
  140.     more_start = 1;
  141.     return winh - hwinh;
  142. }
  143.  
  144. /* this is the messy part. */
  145. /* this define belongs where it's used!!! */
  146.  
  147. #define nextline                \
  148. {                        \
  149.     *s = '\0';                    \
  150.     if (++ln == winh)                \
  151.     if (nvln != -1) {            \
  152.         ln--;        /* too eager */    \
  153.         break;                \
  154.     } else                     \
  155.         ln = scrollwindow(0);        \
  156.     if (!nbuf[ln])                \
  157.     nbuf[ln] = (char *)zalloc(winw + 1);    \
  158.     s = (unsigned char *)nbuf[ln];        \
  159.     sen = s + winw;                \
  160. }
  161.  
  162. #define snextline                \
  163. {                        \
  164.     *s = '\0';                    \
  165.     if (++ln == winh)                \
  166.     if (tosln <= nvln + 1) {        \
  167.         ln = scrollwindow(0);        \
  168.         if (nvln)                \
  169.         nvln--, tosln--;        \
  170.     } else {                \
  171.         tosln--;                \
  172.         ln = scrollwindow(tosln);        \
  173.     }                    \
  174.     if (!nbuf[ln])                \
  175.     nbuf[ln] = (char *)zalloc(winw + 1);    \
  176.     s = (unsigned char *)nbuf[ln];        \
  177.     sen = s + winw;                \
  178. }
  179.  
  180. #ifdef TIOCGWINSZ
  181. int winchanged;            /* window size changed */
  182. #endif
  183.  
  184. int cleareol,            /* clear to end-of-line (if can't cleareod) */
  185.     clearf = 0,            /* alwayslastprompt used immediately before */
  186.     hasam;            /* terminal should have auto-margin        */
  187. static int put_rpmpt,        /* whether we should display right-prompt   */
  188.     oput_rpmpt;            /* whether displayed right-prompt last time */
  189. extern int clearflag;        /* set to non-zero if alwayslastprompt used */
  190.  
  191. /**/
  192. void
  193. refresh(void)
  194. {
  195.     static int inlist;        /* avoiding recursion                        */
  196.     int ln = 0,            /* current line we're working on         */
  197.     nvcs = 0, nvln = -1,    /* video cursor column and line             */
  198.     t0 = -1,        /* tmp                         */
  199.     tosln;            /* tmp in statusline stuff             */
  200.     unsigned char *s,        /* pointer into the video buffer         */
  201.     *t,            /* pointer into the real buffer             */
  202.     *sen,            /* pointer to end of the video buffer (eol)  */
  203.     *scs;            /* pointer to cursor position in real buffer */
  204.     char **qbuf;        /* tmp                         */
  205.  
  206.     /* If this is called from listmatches() (indirectly via trashzle()), and *
  207.      * that was called from the end of refresh(), then we don't need to do   *
  208.      * anything.  All this `inlist' code is actually unnecessary, but it     *
  209.      * improves speed a little in a common case.                             */
  210.     if (inlist)
  211.     return;
  212.  
  213. #ifdef HAVE_SELECT
  214.     cost = 0;            /* reset */
  215. #endif
  216.     cleareol = 0;        /* unset */
  217.     more_start = more_end = 0;    /* unset */
  218.     baudrate = getiparam("BAUD");
  219.     if (resetneeded) {
  220.     setterm();
  221. #ifdef TIOCGWINSZ
  222.     if (winchanged) {
  223.         moveto(0, 0);
  224.         t0 = olnct;        /* this is to clear extra lines even when */
  225.         winchanged = 0;    /* the terminal cannot TCCLEAREOD      */
  226.     }
  227. #endif
  228.     resetvideo();
  229.     resetneeded = 0;    /* unset */
  230.     oput_rpmpt = 0;        /* no right-prompt currently on screen */
  231.         if (!clearflag)
  232.             if (tccan(TCCLEAREOD))
  233.                 tcout(TCCLEAREOD);
  234.             else
  235.                 cleareol = 1;   /* request: clear to end of line */
  236.         if (t0 > -1)
  237.             olnct = t0;
  238.         if (isset(SINGLELINEZLE) || termok != TERM_OK)
  239.             vcs = 0;
  240.         else if (!clearflag && lpptlen) {
  241.             fwrite(lpptbuf, lpptlen, 1, shout);
  242.         fflush(shout);
  243.     }
  244.     if (clearflag) {
  245.         putc('\r', shout);
  246.         vcs = 0;
  247.         moveto(0, pptw);
  248.     }
  249.     clearf = clearflag;
  250.     } else if (winw != columns)
  251.     resetvideo();
  252.  
  253. /* now winw equals columns; now all width comparisons can be made to winw */
  254.  
  255.     if (isset(SINGLELINEZLE) || termok != TERM_OK) {
  256.     singlerefresh();
  257.     return;
  258.     }
  259.  
  260.     if (cs < 0) {
  261. #ifdef DEBUG
  262.     fprintf(stderr, "BUG: negative cursor position\n");
  263.     fflush(stderr); 
  264. #endif
  265.     cs = 0;
  266.     }
  267.     scs = line + cs;
  268.  
  269. /* first, we generate the video line buffers so we know what to put on
  270.    the screen - also determine final cursor position (nvln, nvcs) */
  271.  
  272.     /* Deemed necessary by PWS 1995/05/15 due to kill-line problems */
  273.     if (!*nbuf)
  274.     *nbuf = (char *)zalloc(winw + 1);
  275.  
  276.     s = (unsigned char *)(nbuf[ln = 0] + pptw);
  277.     t = line;
  278.     sen = (unsigned char *)(*nbuf + winw);
  279.     for (; t < line+ll; t++) {
  280.     if (t == scs)            /* if cursor is here, remember it */
  281.         nvcs = s - (unsigned char *)(nbuf[nvln = ln]);
  282.  
  283.     if (*t == '\n')            /* newline */
  284.         nextline
  285.     else if (*t == '\t') {        /* tab */
  286.         t0 = (char *)s - nbuf[ln];
  287.         if ((t0 | 7) + 1 >= winw)
  288.         nextline
  289.         else
  290.         do
  291.             *s++ = ' ';
  292.         while ((++t0) & 7);
  293.     } else if (icntrl(*t)) {    /* other control character */
  294.         *s++ = '^';
  295.         if (s == sen)
  296.         nextline
  297.         *s++ = (*t == 127) ? '?' : (*t | '@');
  298.     } else                /* normal character */
  299.         *s++ = *t;
  300.     if (s == sen)
  301.         nextline
  302.     }
  303.  
  304. /* if we're really on the next line, don't fake it; do everything properly */
  305.     if (t == scs && (nvcs = s - (unsigned char *)(nbuf[nvln = ln])) == winw) {
  306.     *s = '\0';
  307.     if (!nbuf[++ln])
  308.         nbuf[ln] = (char *)zalloc(winw + 1);
  309.     s = (unsigned char *)nbuf[ln];
  310.     nvcs = 0;
  311.     nvln++;
  312.     }
  313.  
  314.     if (t != line + ll)
  315.     more_end = 1;
  316.  
  317.     if (statusline) {
  318.     tosln = ln + 1;
  319.     snextline
  320.     t = (unsigned char *)statusline;
  321.     for (; t < (unsigned char *)statusline+statusll; t++) {
  322.         if (icntrl(*t)) {    /* simplified processing in the status line */
  323.         *s++ = '^';
  324.         if (s == sen)
  325.             snextline
  326.         *s++ = (*t == 127) ? '?' : (*t | '@');
  327.         } else
  328.         *s++ = *t;
  329.         if (s == sen)
  330.         snextline
  331.     }
  332.     }
  333.  
  334. /* insert <.... at end of last line if there is more text past end of screen */
  335.     if (more_end) {
  336.     if (!statusline)
  337.         tosln = winh;
  338.     strncpy(nbuf[tosln - 1] + winw - 7, " <.... ", 7);
  339.     nbuf[tosln - 1][winw] = '\0';
  340.     }
  341.  
  342.     *s = '\0';
  343.     nlnct = ln + 1;
  344.     for (ln = nlnct; ln < winh; ln++)
  345.     zfree(nbuf[ln], winw + 1), nbuf[ln] = NULL;
  346.  
  347. /* determine whether the right-prompt exists and can fit on the screen */
  348.     if (!more_start)
  349.     put_rpmpt = rpptlen && (int)strlen(nbuf[0]) + rpw < winw - 1;
  350.     else {
  351. /* insert >.... on first line if there is more text before start of screen */
  352.     memset(nbuf[0], ' ', pptw);
  353.     t0 = winw - pptw;
  354.     t0 = t0 > 5 ? 5 : t0;
  355.     strncpy(nbuf[0] + pptw, ">....", t0);
  356.     memset(nbuf[0] + pptw + t0, ' ', winw - t0 - pptw);
  357.     nbuf[0][winw] = '\0';
  358.     }
  359.  
  360.     for (ln = 0; !clearf && (ln < nlnct); ln++) {
  361.     /* if we have more lines than last time, clear the newly-used lines */
  362.     if (ln >= olnct)
  363.         cleareol = 1;
  364.  
  365.     /* if old line and new line are different,
  366.        see if we can insert/delete a line to speed up update */
  367.  
  368.     if (ln < olnct - 1 && !(hasam && vcs == winw) &&
  369.         nbuf[ln] && obuf[ln] &&
  370.         strncmp(nbuf[ln], obuf[ln], 16)) {
  371.         if (tccan(TCDELLINE) && obuf[ln + 1] && obuf[ln + 1][0] &&
  372.         nbuf[ln] && !strncmp(nbuf[ln], obuf[ln + 1], 16)) {
  373.         moveto(ln, 0);
  374.         tcout(TCDELLINE);
  375.         zfree(obuf[ln], winw + 1);
  376.         for (t0 = ln; t0 != olnct; t0++)
  377.             obuf[t0] = obuf[t0 + 1];
  378.         obuf[--olnct] = NULL;
  379.         }
  380.     /* don't try to insert a line if olnct = vmaxln (vmaxln is the number
  381.        of lines that have been displayed by this routine) so that we don't
  382.        go off the end of the screen. */
  383.  
  384.         else if (tccan(TCINSLINE) && olnct < vmaxln && nbuf[ln + 1] &&
  385.              obuf[ln] && !strncmp(nbuf[ln + 1], obuf[ln], 16)) {
  386.         moveto(ln, 0);
  387.         tcout(TCINSLINE);
  388.         for (t0 = olnct; t0 != ln; t0--)
  389.             obuf[t0] = obuf[t0 - 1];
  390.         obuf[ln] = NULL;
  391.         olnct++;
  392.         }
  393.     }
  394.  
  395.     /* update the single line */
  396.     refreshline(ln);
  397.  
  398.     /* output the right-prompt if appropriate */
  399.     if (put_rpmpt && !ln && !oput_rpmpt) {
  400.         moveto(0, winw - 1 - rpw);
  401.         fwrite(rpptbuf, rpptlen, 1, shout);
  402.         vcs = winw - 1;
  403.     /* reset character attributes to that set by the main prompt */
  404.         txtchange = pmpt_attr;
  405.         if (txtchangeisset(TXTNOBOLDFACE) && (rpmpt_attr & TXTBOLDFACE))
  406.         tsetcap(TCALLATTRSOFF, 0);
  407.         if (txtchangeisset(TXTNOSTANDOUT) && (rpmpt_attr & TXTSTANDOUT))
  408.         tsetcap(TCSTANDOUTEND, 0);
  409.         if (txtchangeisset(TXTNOUNDERLINE) && (rpmpt_attr & TXTUNDERLINE))
  410.         tsetcap(TCUNDERLINEEND, 0);
  411.         if (txtchangeisset(TXTBOLDFACE) && (rpmpt_attr & TXTNOBOLDFACE))
  412.         tsetcap(TCBOLDFACEBEG, 0);
  413.         if (txtchangeisset(TXTSTANDOUT) && (rpmpt_attr & TXTNOSTANDOUT))
  414.         tsetcap(TCSTANDOUTBEG, 0);
  415.         if (txtchangeisset(TXTUNDERLINE) && (rpmpt_attr & TXTNOUNDERLINE))
  416.         tsetcap(TCUNDERLINEBEG, 0);
  417.     }
  418.     }
  419.  
  420. /* if old buffer had extra lines, set them to be cleared and refresh them
  421. individually */
  422.  
  423.     if (olnct > nlnct) {
  424.     cleareol = 1;
  425.     for (ln = nlnct; ln < olnct; ln++)
  426.         refreshline(ln);
  427.     }
  428.  
  429. /* reset character attributes */
  430.     if (clearf && postedit) {
  431.     if ((txtchange = pmpt_attr ? pmpt_attr : rpmpt_attr)) {
  432.         if (txtchangeisset(TXTNOBOLDFACE))
  433.         tsetcap(TCALLATTRSOFF, 0);
  434.         if (txtchangeisset(TXTNOSTANDOUT))
  435.         tsetcap(TCSTANDOUTEND, 0);
  436.         if (txtchangeisset(TXTNOUNDERLINE))
  437.         tsetcap(TCUNDERLINEEND, 0);
  438.         if (txtchangeisset(TXTBOLDFACE))
  439.         tsetcap(TCBOLDFACEBEG, 0);
  440.         if (txtchangeisset(TXTSTANDOUT))
  441.         tsetcap(TCSTANDOUTBEG, 0);
  442.         if (txtchangeisset(TXTUNDERLINE))
  443.         tsetcap(TCUNDERLINEBEG, 0);
  444.     }
  445.     }
  446.     clearf = 0;
  447.  
  448. /* move to the new cursor position */
  449.     moveto(nvln, nvcs);
  450.  
  451. /* swap old and new buffers - better than freeing/allocating every time */
  452.     qbuf = nbuf;
  453.     nbuf = obuf;
  454.     obuf = qbuf;
  455. /* store current values so we can use them next time */
  456.     ovln = nvln;
  457.     olnct = nlnct;
  458.     oput_rpmpt = put_rpmpt;
  459.     if (nlnct > vmaxln)
  460.     vmaxln = nlnct;
  461.     fflush(shout);        /* make sure everything is written out */
  462.  
  463.     /* if we have a new list showing, note it; if part of the list has been
  464.     overwritten, redisplay it. */
  465.     if (showinglist == -2 || (showinglist > 0 && showinglist < nlnct)) {
  466.     inlist = 1;
  467.     listmatches();
  468.     inlist = 0;
  469.     refresh();
  470.     }
  471.     if (showinglist == -1)
  472.     showinglist = nlnct;
  473. }
  474.  
  475. #define tcinscost(X)   (tccan(TCMULTINS) ? tclen[TCMULTINS] : (X)*tclen[TCINS])
  476. #define tcdelcost(X)   (tccan(TCMULTDEL) ? tclen[TCMULTDEL] : (X)*tclen[TCDEL])
  477. #define tc_delchars(X)    (void) tcmultout(TCDEL, TCMULTDEL, (X))
  478. #define tc_inschars(X)    (void) tcmultout(TCINS, TCMULTINS, (X))
  479. #define tc_upcurs(X)    (void) tcmultout(TCUP, TCMULTUP, (X))
  480. #define tc_leftcurs(X)    (void) tcmultout(TCLEFT, TCMULTLEFT, (X))
  481.  
  482. /* refresh one line, using whatever speed-up tricks are provided by the tty */
  483.  
  484. /**/
  485. void
  486. refreshline(int ln)
  487. {
  488.     char *nl, *ol, *p1;        /* line buffer pointers             */
  489.     int ccs = 0,        /* temporary count for cursor position     */
  490.     char_ins = 0,        /* number of characters inserted/deleted */
  491.     col_cleareol,        /* clear to end-of-line from this column */
  492.     i, j,            /* tmp                     */
  493.     nllen, ollen;        /* new and old line buffer lengths     */
  494.  
  495. /* 0: setup */
  496.     nl = nbuf[ln];
  497.     nllen = nl ? strlen(nl) : 0;
  498.     ol = obuf[ln] ? obuf[ln] : "";
  499.     ollen = strlen(ol);
  500.  
  501. /* 1: pad out the new buffer with spaces to contain _all_ of the characters
  502.       which need to be written. do this now to allow some pre-processing */
  503.  
  504.     if (cleareol         /* request to clear to end of line */
  505.     || !nllen         /* no line buffer given */
  506.     || (ln == 0 && (put_rpmpt != oput_rpmpt))) {    /* prompt changed */
  507.     p1 = halloc(winw + 1);
  508.     if (nllen)
  509.         strncpy(p1, nl, nllen);
  510.     memset(p1 + nllen, ' ', winw - nllen);
  511.     p1[winw] = '\0';
  512.     if (ln && nbuf[ln])
  513.         strncpy(nl, p1, winw + 1);    /* next time obuf will be up-to-date */
  514.     else
  515.         nl = p1;        /* shouldn't happen */
  516.     nllen = winw;
  517.     } else if (ollen > nllen) { /* make new line at least as long as old */
  518.     p1 = halloc(ollen + 1);
  519.     strncpy(p1, nl, nllen);
  520.     memset(p1 + nllen, ' ', ollen - nllen);
  521.     p1[ollen] = '\0';
  522.     nl = p1;
  523.     nllen = ollen;
  524.     }
  525.  
  526. /* 2: see if we can clear to end-of-line, and if it's faster, work out where
  527.    to do it from - we can normally only do so if there's no right-prompt.
  528.    With automatic margins, we shouldn't do it if there is another line, in
  529.    case it messes up cut and paste. */
  530.  
  531.     if (hasam && ln < nlnct - 1)
  532.     col_cleareol = -2;    /* clearing eol would be evil so don't */
  533.     else {
  534.     col_cleareol = -1;
  535.     if (tccan(TCCLEAREOL) && (nllen == winw || put_rpmpt != oput_rpmpt)) {
  536.         for (i = nllen; i && nl[i - 1] == ' '; i--);
  537.         for (j = ollen; j && ol[j - 1] == ' '; j--);
  538.         if ((j > i + tclen[TCCLEAREOL])    /* new buf has enough spaces */
  539.         || (nllen == winw && nl[winw - 1] == ' '))
  540.         col_cleareol = i;
  541.     }
  542.     }
  543.  
  544. /* 3: main display loop - write out the buffer using whatever tricks we can */
  545.  
  546.     for (;;) {
  547.     /* skip past all matching characters */
  548.     for (; *nl && (*nl == *ol); nl++, ol++, ccs++) ;
  549.  
  550.     if (!*nl) {
  551.         if ((char_ins <= 0)    || (ccs >= winw))    /* written everything */
  552.         return;
  553.         else        /* we've got junk on the right yet to clear */
  554.         if (tccan(TCCLEAREOL) && (char_ins >= tclen[TCCLEAREOL])
  555.                 && col_cleareol != -2)
  556.             col_cleareol = 0;    /* force a clear to end of line */
  557.     }
  558.  
  559.     moveto(ln, ccs);    /* move to where we do all output from */
  560.  
  561.     /* if we can finish quickly, do so */
  562.     if ((col_cleareol >= 0) && (ccs >= col_cleareol)) {
  563.         tcout(TCCLEAREOL);
  564.         SELECT_ADD_COST(tclen[TCCLEAREOL]);
  565.         return;
  566.     }
  567.  
  568.     /* we've written out the new but yet to clear rubbish due to inserts */
  569.     if (!*nl) {
  570.         i = (winw - ccs < char_ins) ? (winw - ccs) : char_ins;
  571.         if (tccan(TCDEL) && (tcdelcost(i) <= i + 1))
  572.         tc_delchars(i);
  573.         else {
  574.         SELECT_ADD_COST(i);
  575.         vcs += i;
  576.         while (i-- > 0)
  577.             putc(' ', shout);
  578.         }
  579.         return;
  580.     }
  581.  
  582.     /* if we've reached the end of the old buffer, then there are few tricks
  583.        we can do, so we just dump out what we must and clear if we can */
  584.     if (!*ol) {
  585.         i = (col_cleareol >= 0) ? col_cleareol : nllen;
  586.         i -= ccs;
  587.         fwrite(nl, i, 1, shout);
  588.         SELECT_ADD_COST(i);
  589.         vcs += i;
  590.         if (col_cleareol >= 0) {
  591.         tcout(TCCLEAREOL);
  592.         SELECT_ADD_COST(tclen[TCCLEAREOL]);
  593.         }
  594.         return;
  595.     }
  596.  
  597.     /* inserting & deleting chars: we can if there's no right-prompt */
  598.     if ((ln || !put_rpmpt || !oput_rpmpt)) {
  599.  
  600.     /* deleting characters - see if we can find a match series that
  601.        makes it cheaper to delete intermediate characters
  602.        eg. oldline: hifoobar } hopefully cheaper here to delete two
  603.            newline: foobar     } characters, then we have six matches */
  604.  
  605.         if (tccan(TCDEL) && nl[1] && ol[1] && (ol[1] != nl[1])) {
  606.         for (i = 0, p1 = ol; *p1; p1++, i++)
  607.             if (tcdelcost(i) < pfxlen(p1, nl)) {
  608.             tc_delchars(i);
  609.             SELECT_ADD_COST(i);
  610.             ol = p1;
  611.             char_ins -= i;
  612.             break;
  613.             }
  614.         if (*p1)
  615.             continue;
  616.         }
  617.     /* inserting characters - characters pushed off the right should be
  618.        annihilated, but we don't do this if we're on the last line lest
  619.        undesired scrolling occurs due to `illegal' characters on screen */
  620.  
  621.         if ((vln != lines - 1) &&    /* not on last line */
  622.         tccan(TCINS) && nl[1] && ol[1] && (ol[1] != nl[1])) {
  623.         for (i = 0, p1 = nl; *p1; p1++, i++)
  624.             if (tcinscost(i) < pfxlen(p1, ol)) {
  625.             tc_inschars(i);
  626.             SELECT_ADD_COST(2 * i);
  627.             fwrite(nl, i, 1, shout);
  628.             ccs = (vcs += i);
  629.             nl = p1;
  630.             char_ins += i;
  631.             break;
  632.             }
  633.         if (*p1)
  634.             continue;
  635.         }
  636.     }
  637.     /* we can't do any fancy tricks, so just dump the single character
  638.        and keep on trying */
  639.     putc(*nl, shout);
  640.     SELECT_ADD_COST(1);
  641.     nl++, ol++;
  642.     ccs++, vcs++;
  643.     }
  644. }
  645.  
  646. /* move the cursor to line ln (relative to the prompt line),
  647.    absolute column cl; update vln, vcs - video line and column */
  648.  
  649. /**/
  650. void
  651. moveto(int ln, int cl)
  652. {
  653.     if (vcs == winw) {
  654.     vln++, vcs = 0;
  655.     if (!hasam) {
  656.         putc('\r', shout);
  657.         putc('\n', shout);
  658.     } else {
  659.         if ((vln < nlnct) && nbuf[vln] && *nbuf[vln])
  660.         putc(*nbuf[vln], shout);
  661.         else
  662.         putc(' ', shout);
  663.         putc('\r', shout);
  664.         if ((vln < olnct) && obuf[vln] && *obuf[vln])
  665.         *obuf[vln] = *nbuf[vln];
  666.     }
  667.     SELECT_ADD_COST(2);
  668.     }
  669.  
  670.     if (ln == vln && cl == vcs)
  671.     return;
  672.  
  673. /* move up */
  674.     if (ln < vln) {
  675.     tc_upcurs(vln - ln);
  676.     vln = ln;
  677.     }
  678. /* move down; if we might go off the end of the screen, use newlines
  679.    instead of TCDOWN */
  680.  
  681.     while (ln > vln) {
  682.     if (vln < vmaxln - 1)
  683.         if (ln > vmaxln - 1) {
  684.         if (tc_downcurs(vmaxln - 1 - vln))
  685.             vcs = 0;
  686.         vln = vmaxln - 1;
  687.         } else {
  688.         if (tc_downcurs(ln - vln))
  689.             vcs = 0;
  690.         vln = ln;
  691.         continue;
  692.         }
  693.     putc('\r', shout), vcs = 0; /* safety precaution */
  694.     SELECT_ADD_COST(1);
  695.     while (ln > vln) {
  696.         putc('\n', shout);
  697.         SELECT_ADD_COST(1);
  698.         vln++;
  699.     }
  700.     }
  701.  
  702.     if (cl == vcs)
  703.     return;
  704.  
  705. /* choose cheapest movements for ttys without multiple movement capabilities -
  706.    do this now because it's easier (to code) */
  707.     if (cl <= vcs / 2) {
  708.     putc('\r', shout);
  709.     SELECT_ADD_COST(1);
  710.     vcs = 0;
  711.     }
  712.     if (vcs < cl)
  713.     tc_rightcurs(cl);
  714.     else if (vcs > cl)
  715.     tc_leftcurs(vcs - cl);
  716.     vcs = cl;
  717. }
  718.  
  719. /**/
  720. int
  721. tcmultout(int cap, int multcap, int ct)
  722. {
  723.     if (tccan(multcap) && (!tccan(cap) || tclen[multcap] < tclen[cap] * ct)) {
  724.     tcoutarg(multcap, ct);
  725.     SELECT_ADD_COST(tclen[multcap]);
  726.     return 1;
  727.     } else if (tccan(cap)) {
  728.     SELECT_ADD_COST((tclen[cap] * ct));
  729.     while (ct--)
  730.         tcout(cap);
  731.     return 1;
  732.     }
  733.     return 0;
  734. }
  735.  
  736. /**/
  737. void
  738. tc_rightcurs(int cl)
  739. {
  740.     int ct = cl - vcs,        /* number of characters to move across        */
  741.     horz_tabs = 0,        /* number of horizontal tabs if we do them  */
  742.     i = vcs,        /* cursor position after initial movements  */
  743.     j = 0;            /* number of chars outputted if we use tabs */
  744.     char *t;
  745.  
  746. /* calculate how many horizontal tabs it would take, if we can do them -
  747.    tabs are assumed to be 8 spaces */
  748.     if (tccan(TCNEXTTAB) && ((vcs | 7) < cl)) {
  749.     horz_tabs = 1;
  750.     i = (vcs | 7) + 1;
  751.     for (; i + 8 <= cl; i += 8)
  752.         horz_tabs++;
  753.     j = cl - i;        /* number of chars after last tab */
  754.     if (tccan(TCRIGHT))
  755.         j *= tclen[TCRIGHT];
  756.     j += (horz_tabs * tclen[TCNEXTTAB]); /* # of chars if we use tabs */
  757.     }
  758.  
  759. /* do a multright if we can - if it's cheaper or we can't use other tricks */
  760.     if (tccan(TCMULTRIGHT) &&
  761.     (!tccan(TCRIGHT) || (tclen[TCMULTRIGHT] < tclen[TCRIGHT] * ct) ||
  762.      !tccan(TCNEXTTAB) || (tclen[TCMULTRIGHT] < j))) {
  763.     tcoutarg(TCMULTRIGHT, ct);
  764.     SELECT_ADD_COST(tclen[TCMULTRIGHT]);
  765.     return;
  766.     }
  767.  
  768. /* try to go with tabs if a multright is not feasible/convenient */
  769.     if (horz_tabs) {
  770.     SELECT_ADD_COST((tclen[TCNEXTTAB] * horz_tabs));
  771.     for (; horz_tabs--;)
  772.         tcout(TCNEXTTAB);
  773.     if ((ct = cl - i) == 0) /* number of chars still to move across */
  774.         return;
  775.     }
  776.  
  777. /* or try to dump lots of right movements */
  778.     if (tccan(TCRIGHT)) {
  779.     SELECT_ADD_COST((tclen[TCRIGHT] * ct));
  780.     for (; ct--;)
  781.         tcout(TCRIGHT);
  782.     return;
  783.     }
  784.  
  785. /* otherwise _carefully_ write the contents of the video buffer */
  786.     SELECT_ADD_COST(ct);
  787.     for (j = 0, t = nbuf[vln]; *t && (j < i); j++, t++);
  788.     if (j == i)
  789.     for ( ; *t && ct; ct--, t++)
  790.         putc(*t, shout);
  791.     while (ct--)
  792.     putc(' ', shout);    /* not my fault your terminal can't go right */
  793. }
  794.  
  795. /**/
  796. int
  797. tc_downcurs(int ct)
  798. {
  799.     int ret = 0;
  800.  
  801.     if (ct && !tcmultout(TCDOWN, TCMULTDOWN, ct)) {
  802.     SELECT_ADD_COST(ct + 1);
  803.     while (ct--)
  804.         putc('\n', shout);
  805.     putc('\r', shout), ret = -1;
  806.     }
  807.     return ret;
  808. }
  809.  
  810. /**/
  811. void
  812. tcout(int cap)
  813. {
  814.     tputs(tcstr[cap], 1, putshout);
  815. }
  816.  
  817. /**/
  818. void
  819. tcoutarg(int cap, int arg)
  820. {
  821.     tputs(tgoto(tcstr[cap], arg, arg), 1, putshout);
  822. }
  823.  
  824. /**/
  825. void
  826. clearscreen(void)
  827. {
  828.     tcout(TCCLEARSCREEN);
  829.     resetneeded = 1;
  830.     clearflag = 0;
  831. }
  832.  
  833. /**/
  834. void
  835. redisplay(void)
  836. {
  837.     moveto(0, pptw);
  838.     putc('\r', shout);
  839.     resetneeded = 1;
  840.     clearflag = 0;
  841. }
  842.  
  843. /**/
  844. void
  845. singlerefresh(void)
  846. {
  847.     char *vbuf, *vp,        /* video buffer and pointer    */
  848.     **qbuf,            /* tmp                   */
  849.     *refreshop = *obuf;    /* pointer to old video buffer */
  850.     int t0,            /* tmp                   */
  851.     vsiz,            /* size of new video buffer    */
  852.     nvcs = 0;        /* new video cursor column     */
  853.  
  854.     nlnct = 1;
  855. /* generate the new line buffer completely */
  856.     for (vsiz = 1 + pptw, t0 = 0; t0 != ll; t0++, vsiz++)
  857.     if (line[t0] == '\t')
  858.         vsiz = (vsiz | 7) + 1;
  859.     else if (icntrl(line[t0]))
  860.         vsiz++;
  861.     vbuf = (char *)zalloc(vsiz);
  862.  
  863.     if (cs < 0) {
  864. #ifdef DEBUG
  865.     fprintf(stderr, "BUG: negative cursor position\n");
  866.     fflush(stderr); 
  867. #endif
  868.     cs = 0;
  869.     }
  870.  
  871.     memcpy(vbuf, lpptbuf + lpptlen - pptw, pptw); /* only use last part of prompt */
  872.     vbuf[pptw] = '\0';
  873.     vp = vbuf + pptw;
  874.  
  875.     for (t0 = 0; t0 != ll; t0++) {
  876.     if (line[t0] == '\t')
  877.         for (*vp++ = ' '; (vp - vbuf) & 7; )
  878.         *vp++ = ' ';
  879.     else if (line[t0] == '\n') {
  880.         *vp++ = '\\';
  881.         *vp++ = 'n';
  882.     } else if (line[t0] == 0x7f) {
  883.         *vp++ = '^';
  884.         *vp++ = '?';
  885.     } else if (icntrl(line[t0])) {
  886.         *vp++ = '^';
  887.         *vp++ = line[t0] | '@';
  888.     } else
  889.         *vp++ = line[t0];
  890.     if (t0 == cs)
  891.         nvcs = vp - vbuf - 1;
  892.     }
  893.     if (t0 == cs)
  894.     nvcs = vp - vbuf;
  895.     *vp = '\0';
  896.  
  897. /* determine which part of the new line buffer we want for the display */
  898.     if ((winpos && nvcs < winpos + 1) || (nvcs > winpos + winw - 2)) {
  899.     if ((winpos = nvcs - ((winw - hasam) / 2)) < 0)
  900.         winpos = 0;
  901.     }
  902.     if (winpos)
  903.     vbuf[winpos] = '<';    /* line continues to the left */
  904.     if ((int)strlen(vbuf + winpos) > (winw - hasam)) {
  905.     vbuf[winpos + winw - hasam - 1] = '>';    /* line continues to right */
  906.     vbuf[winpos + winw - hasam] = '\0';
  907.     }
  908.     strcpy(nbuf[0], vbuf + winpos);
  909.     zfree(vbuf, vsiz);
  910.     nvcs -= winpos;
  911.  
  912. /* display the `visable' portion of the line buffer */
  913.     for (t0 = 0, vp = *nbuf;;) {
  914.     /* skip past all matching characters */
  915.     for (; *vp && *vp == *refreshop; t0++, vp++, refreshop++) ;
  916.  
  917.     if (!*vp && !*refreshop)
  918.         break;
  919.  
  920.     singmoveto(t0);        /* move to where we do all output from */
  921.  
  922.     if (!*refreshop) {
  923.         if ((t0 = strlen(vp)))
  924.         fwrite(vp, t0, 1, shout);
  925.         vcs += t0;
  926.         break;
  927.     }
  928.     if (!*vp) {
  929.         if (tccan(TCCLEAREOL))
  930.         tcout(TCCLEAREOL);
  931.         else
  932.         for (; *refreshop++; vcs++)
  933.             putc(' ', shout);
  934.         break;
  935.     }
  936.     putc(*vp, shout);
  937.     vcs++, t0++;
  938.     vp++, refreshop++;
  939.     }
  940. /* move to the new cursor position */
  941.     singmoveto(nvcs);
  942.  
  943.     qbuf = nbuf;
  944.     nbuf = obuf;
  945.     obuf = qbuf;
  946.     fflush(shout);        /* make sure everything is written out */
  947. }
  948.  
  949. /**/
  950. void
  951. singmoveto(int pos)
  952. {
  953.     if (pos == vcs)
  954.     return;
  955.     if (pos <= vcs / 2) {
  956.     putc('\r', shout);
  957.     vcs = 0;
  958.     }
  959.     if (pos < vcs) {
  960.     tc_leftcurs(vcs - pos);
  961.     vcs = pos;
  962.     }
  963.     if (pos > vcs) {
  964.     if (tcmultout(TCRIGHT, TCMULTRIGHT, pos - vcs))
  965.         vcs = pos;
  966.     else
  967.         while (pos > vcs) {
  968.         putc(nbuf[0][vcs], shout);
  969.         vcs++;
  970.         }
  971.     }
  972. }
  973.  
  974. /* generate left and right prompts */
  975.  
  976. /**/
  977. void
  978. genprompts(void)
  979. {
  980.     zsfree(lpptbuf);
  981.     zsfree(rpptbuf);
  982.     lpptbuf = putprompt(lpmpt, &lpptlen, &pptw, 1);
  983.     pmpt_attr = txtchange;
  984.     rpptbuf = putprompt(rpmpt, &rpptlen, &rpw, 1);
  985.     rpmpt_attr = txtchange;
  986. }
  987.